home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / launchpadlib / credentials.py < prev    next >
Text File  |  2009-07-13  |  10KB  |  247 lines

  1. # Copyright 2008 Canonical Ltd.
  2.  
  3. # This file is part of launchpadlib.
  4. #
  5. # launchpadlib is free software: you can redistribute it and/or modify it
  6. # under the terms of the GNU Lesser General Public License as published by the
  7. # Free Software Foundation, version 3 of the License.
  8. #
  9. # launchpadlib is distributed in the hope that it will be useful, but WITHOUT
  10. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  12. # for more details.
  13. #
  14. # You should have received a copy of the GNU Lesser General Public License
  15. # along with launchpadlib. If not, see <http://www.gnu.org/licenses/>.
  16.  
  17. """launchpadlib credentials and authentication support."""
  18.  
  19. __metaclass__ = type
  20. __all__ = [
  21.     'AccessToken',
  22.     'Consumer',
  23.     'Credentials',
  24.     ]
  25.  
  26. from ConfigParser import SafeConfigParser
  27. import cgi
  28. import httplib2
  29. from oauth.oauth import OAuthConsumer, OAuthToken
  30. from urllib import urlencode
  31.  
  32. from lazr.restfulclient.errors import CredentialsFileError, HTTPError
  33.  
  34.  
  35. CREDENTIALS_FILE_VERSION = '1'
  36. STAGING_WEB_ROOT = 'https://staging.launchpad.net/'
  37. request_token_page = '+request-token'
  38. access_token_page = '+access-token'
  39. authorize_token_page = '+authorize-token'
  40.  
  41.  
  42. class Credentials:
  43.     """Standard credentials storage and usage class.
  44.  
  45.     :ivar consumer: The consumer (application)
  46.     :type consumer: `Consumer`
  47.     :ivar access_token: Access information on behalf of the user
  48.     :type access_token: `AccessToken`
  49.     """
  50.     _request_token = None
  51.  
  52.     def __init__(self, consumer_name=None, consumer_secret='',
  53.                  access_token=None):
  54.         """The user's Launchpad API credentials.
  55.  
  56.         :param consumer_name: The name of the consumer (application)
  57.         :param consumer_secret: The secret of the consumer
  58.         :param access_token: The authenticated user access token
  59.         :type access_token: `AccessToken`
  60.         """
  61.         self.consumer = None
  62.         if consumer_name is not None:
  63.             self.consumer = Consumer(consumer_name, consumer_secret)
  64.         self.access_token = access_token
  65.  
  66.     def load(self, readable_file):
  67.         """Load credentials from a file-like object.
  68.  
  69.         This overrides the consumer and access token given in the constructor
  70.         and replaces them with the values read from the file.
  71.  
  72.         :param readable_file: A file-like object to read the credentials from
  73.         :type readable_file: Any object supporting the file-like `read()`
  74.             method
  75.         """
  76.         # Attempt to load the access token from the file.
  77.         parser = SafeConfigParser()
  78.         parser.readfp(readable_file)
  79.         # Check the version number and extract the access token and
  80.         # secret.  Then convert these to the appropriate instances.
  81.         if not parser.has_section(CREDENTIALS_FILE_VERSION):
  82.             raise CredentialsFileError('No configuration for version %s' %
  83.                                        CREDENTIALS_FILE_VERSION)
  84.         consumer_key = parser.get(
  85.             CREDENTIALS_FILE_VERSION, 'consumer_key')
  86.         consumer_secret = parser.get(
  87.             CREDENTIALS_FILE_VERSION, 'consumer_secret')
  88.         self.consumer = Consumer(consumer_key, consumer_secret)
  89.         access_token = parser.get(
  90.             CREDENTIALS_FILE_VERSION, 'access_token')
  91.         access_secret = parser.get(
  92.             CREDENTIALS_FILE_VERSION, 'access_secret')
  93.         self.access_token = AccessToken(access_token, access_secret)
  94.  
  95.     @classmethod
  96.     def load_from_path(cls, path):
  97.         """Convenience method for loading credentials from a file.
  98.  
  99.         Open the file, create the Credentials and load from the file,
  100.         and finally close the file and return the newly created
  101.         Credentials instance.
  102.  
  103.         :param path: In which file the credential file should be saved.
  104.         :type path: string
  105.         :return: The loaded Credentials instance.
  106.         :rtype: `Credentials`
  107.         """
  108.         credentials = cls()
  109.         credentials_file = open(path, 'r')
  110.         credentials.load(credentials_file)
  111.         credentials_file.close()
  112.         return credentials
  113.  
  114.     def save(self, writable_file):
  115.         """Write the credentials to the file-like object.
  116.  
  117.         :param writable_file: A file-like object to write the credentials to
  118.         :type writable_file: Any object supporting the file-like `write()`
  119.             method
  120.         :raise CredentialsFileError: when there is either no consumer or no
  121.             access token
  122.         """
  123.         if self.consumer is None:
  124.             raise CredentialsFileError('No consumer')
  125.         if self.access_token is None:
  126.             raise CredentialsFileError('No access token')
  127.  
  128.         parser = SafeConfigParser()
  129.         parser.add_section(CREDENTIALS_FILE_VERSION)
  130.         parser.set(CREDENTIALS_FILE_VERSION,
  131.                    'consumer_key', self.consumer.key)
  132.         parser.set(CREDENTIALS_FILE_VERSION,
  133.                    'consumer_secret', self.consumer.secret)
  134.         parser.set(CREDENTIALS_FILE_VERSION,
  135.                    'access_token', self.access_token.key)
  136.         parser.set(CREDENTIALS_FILE_VERSION,
  137.                    'access_secret', self.access_token.secret)
  138.         parser.write(writable_file)
  139.  
  140.     def save_to_path(self, path):
  141.         """Convenience method for saving credentials to a file.
  142.  
  143.         Create the file, call self.save(), and close the file. Existing
  144.         files are overwritten.
  145.  
  146.         :param path: In which file the credential file should be saved.
  147.         :type path: string
  148.         """
  149.         credentials_file = open(path, 'w')
  150.         self.save(credentials_file)
  151.         credentials_file.close()
  152.  
  153.     def get_request_token(self, context=None, web_root=STAGING_WEB_ROOT):
  154.         """Request an OAuth token to Launchpad.
  155.  
  156.         Also store the token in self._request_token.
  157.  
  158.         This method must not be called on an object with no consumer
  159.         specified or if an access token has already been obtained.
  160.  
  161.         :param context: The context of this token, that is, its scope of
  162.             validity within Launchpad.
  163.         :param web_root: The URL of the website on which the token
  164.             should be requested.
  165.         :return: The URL for the user to authorize the `OAuthToken` provided
  166.             by Launchpad.
  167.         """
  168.         assert self.consumer is not None, "Consumer not specified."
  169.         assert self.access_token is None, "Access token already obtained."
  170.         params = dict(
  171.             oauth_consumer_key=self.consumer.key,
  172.             oauth_signature_method='PLAINTEXT',
  173.             oauth_signature='&')
  174.         url = web_root + request_token_page
  175.         response, content = httplib2.Http().request(
  176.             url, method='POST', body=urlencode(params))
  177.         if response.status != 200:
  178.             raise HTTPError(response, content)
  179.         self._request_token = OAuthToken.from_string(content)
  180.         url = '%s%s?oauth_token=%s' % (web_root, authorize_token_page,
  181.                                        self._request_token.key)
  182.         if context is not None:
  183.             url += "&lp.context=%s" % context
  184.         return url
  185.  
  186.     def exchange_request_token_for_access_token(
  187.         self, web_root=STAGING_WEB_ROOT):
  188.         """Exchange the previously obtained request token for an access token.
  189.  
  190.         This method must not be called unless get_request_token() has been
  191.         called and completed successfully.
  192.  
  193.         The access token will be stored as self.access_token.
  194.  
  195.         :param web_root: The base URL of the website that granted the
  196.             request token.
  197.         """
  198.         assert self._request_token is not None, (
  199.             "get_request_token() doesn't seem to have been called.")
  200.         params = dict(
  201.             oauth_consumer_key=self.consumer.key,
  202.             oauth_signature_method='PLAINTEXT',
  203.             oauth_token=self._request_token.key,
  204.             oauth_signature='&%s' % self._request_token.secret)
  205.         url = web_root + access_token_page
  206.         response, content = httplib2.Http().request(
  207.             url, method='POST', body=urlencode(params))
  208.         if response.status != 200:
  209.             raise HTTPError(response, content)
  210.         self.access_token = AccessToken.from_string(content)
  211.  
  212.  
  213. # These two classes are provided for convenience (so applications don't need
  214. # to import from launchpadlib._oauth.oauth), and to provide a default argument
  215. # for secret.
  216.  
  217. class Consumer(OAuthConsumer):
  218.     """An OAuth consumer (application)."""
  219.  
  220.     def __init__(self, key, secret=''):
  221.         super(Consumer, self).__init__(key, secret)
  222.  
  223.  
  224. class AccessToken(OAuthToken):
  225.     """An OAuth access token."""
  226.  
  227.     def __init__(self, key, secret='', context=None):
  228.         super(AccessToken, self).__init__(key, secret)
  229.         self.context = context
  230.  
  231.     @classmethod
  232.     def from_string(cls, query_string):
  233.         """Create and return a new `AccessToken` from the given string."""
  234.         params = cgi.parse_qs(query_string, keep_blank_values=False)
  235.         key = params['oauth_token']
  236.         assert len(key) == 1, "Query string must have exactly one key."
  237.         key = key[0]
  238.         secret = params['oauth_token_secret']
  239.         assert len(secret) == 1, "Query string must have exactly one secret."
  240.         secret = secret[0]
  241.         context = params.get('lp.context')
  242.         if context is not None:
  243.             assert len(context) == 1, (
  244.                 "Query string must have exactly one context")
  245.             context = context[0]
  246.         return cls(key, secret, context)
  247.